/*ws2811是单总线接口的led驱动芯片，通过单个GPIO模拟高低时序发出颜色值，芯片收到后控制输出对应PWM改变三路RGB灯的颜色*/
/*使用杂项设备+平台总线框架实现该功能*/
/*数据传输时间
1码 高电平 750ns-1.6us  低电平 220ns-420ns
0码 高电平 220ns-420ns  低电平 750ns-1.6us
res 低电平时间 300us以上
*/

#include <linux/kernel.h>
// #include <linux/input.h>
// #include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>              //包含宏定义得头文件
#include <linux/module.h>
#include <linux/platform_device.h>   //平台总线模型文件
#include <linux/of.h>                //设备树操作函数操作集
#include <linux/of_address.h>        //of_iomap
#include <linux/miscdevice.h>        //杂项设备头文件
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/gpio.h>             //gpio子系统头文件
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include "rgb_mode.h"

#define SYS_LED_NAME		"ws2811_led"
#define HIGH                 1  //判断高低电平
#define SEND_TIMES           5 //设置led的个数

int ws2811_gpio=0;              //获取到得到gpio编号
struct device_node*test_device_node;//设备树节点
static u8 WS2811_RGB[3]={0};  // BLUE RED GREEN
unsigned int fl;


//向ws2811芯片发出置低位
static void ws2811_hight(void)
{
	gpio_set_value(ws2811_gpio,1);//设置高电平
	ndelay(850);
	gpio_set_value(ws2811_gpio,0);//设置低电平
	ndelay(320);
}
//向ws2811芯片发出置低位
static void ws2811_low(void)
{
	gpio_set_value(ws2811_gpio,1);//设置高电平
	ndelay(320);
	gpio_set_value(ws2811_gpio,0);//设置低电平
	ndelay(850);
}
//发出Reset信号
static void ws2811_reset(void)
{
	gpio_set_value(ws2811_gpio,0);//设置低电平//拉低
	udelay(300);                    //至少保持50us，之后芯片等待接收新的数据
}
//============================发送RGB灰度数据===================================//
void send_single_data(const u8 *data) 		 	//数据格式:R7~R0~G7~G0~B7~B0
{
	s8 i=0,j=0;
	for(i = 0; i < 3; i++){
		for(j = 7; j > -1; j--){
			if(((data[i])&(HIGH<<j))==(HIGH<<j)) //判断数组存储的数据
				ws2811_hight();
			else 
				ws2811_low();
		}
	}
}

void send_string_data(const u8 *data,u8 size) 	//size:为led灯的个数										
{
	u8 i=0;
	for(i=0;i<size;i++){
		send_single_data(data);	 
	}
	ws2811_reset();
}

//=================================常亮可以同时控制一灯珠三色都亮========= BLUE RED GREEN ===============//
void RGB_Solid_on(char cmd) 
{
    // unsigned int fl;
    int i = 0;
    // local_irq_save(fl);//把当前中断状态保存到flags中，然后禁用当前处理器上的中断发送
    if(cmd==1){                             //红灯
		WS2811_RGB[0]=0xff;
		WS2811_RGB[1]=0;
		WS2811_RGB[2]=0;
		send_string_data(WS2811_RGB,SEND_TIMES);              //发送次数与灯管长度有关
	}
	else if(cmd==2){                        //红+绿
		WS2811_RGB[0]=0;
		WS2811_RGB[1]=255;
		WS2811_RGB[2]=0;
		send_string_data(WS2811_RGB,SEND_TIMES); 
	}
	else if(cmd==3){                        //红+绿+蓝
	    WS2811_RGB[0]=0;
		WS2811_RGB[1]=0;
		WS2811_RGB[2]=255;
		send_string_data(WS2811_RGB,SEND_TIMES); 
	}
    else if(cmd==4){                        //红+绿+蓝
        WS2811_RGB[0]=0;
        WS2811_RGB[1]=0;
        WS2811_RGB[2]=0;
        send_string_data(WS2811_RGB,SEND_TIMES); 
    }
    // local_irq_restore(fl);//中断恢复
}

//=================================RGB闪烁========= BLUE RED GREEN ===============//
void RGB_Lighting(reg_mode rgb)
{
	if(rgb.Light_red){
		WS2811_RGB[0]=255;
		WS2811_RGB[1]=0;
		WS2811_RGB[2]=0;
		send_string_data(WS2811_RGB,SEND_TIMES);              //发送次数与灯管长度有关
		rgb.Light_red=0;
	}
	else if(rgb.Light_green){
		WS2811_RGB[0]=0;
		WS2811_RGB[1]=255;
		WS2811_RGB[2]=0;
		send_string_data(WS2811_RGB,SEND_TIMES); 
		rgb.Light_green=0;
	}
	else if(rgb.Light_blue){
		WS2811_RGB[0]=0;
		WS2811_RGB[1]=0;
		WS2811_RGB[2]=255;
		send_string_data(WS2811_RGB,SEND_TIMES); 
		rgb.Light_blue=0;
	}
}

/*文件操作集的结构体*/
static int ws2811_open(struct inode *node, struct file *filp)
{
    printk("hello cdev opnen\n");
    return 0;
}

static int ws2811_release(struct inode*node,struct file * file)
{
    printk("hello cdev cdev_release byb byb!\n");
    return 0;
}

static ssize_t ws2811_read(struct file*filp, char __user*buf, size_t count, loff_t*f_pos)
{
    char kbuf[64]="hello";
    if(copy_to_user(buf,kbuf,strlen(kbuf)!=0))//拷贝信息到应用层
    {
        printk("copy_to_user error!\n");
        return -1;
    }

    printk("hello cdev_read file!\n");
    return 0;
}

//获取到应用层的数据到内核层在改函数进行io的控制操作
static ssize_t ws2811_write(struct file * file, const char __user *buf,size_t size,loff_t*offset)
{
     char kbuf[64]={0};
    if(copy_from_user(kbuf,buf,size)!=0)//从应用层拷贝数据到内核层
    {
         printk("copy_from_user!\n");
        return -1;
    }
    printk("copy_from_user %s!\n",kbuf);
    RGB_Solid_on(kbuf[0]);//控制彩灯的亮 1、2、3
    // if(kbuf[0]==1)
    //     gpio_set_value(ws2811_gpio,1);//设置高电平
    // else if(kbuf[0]==0)
    //     gpio_set_value(ws2811_gpio,0);//输出低电平
    return 0;
}

//文件操作集
struct file_operations misc_fops={ 
    .owner   = THIS_MODULE,
    .open    = ws2811_open,
    .read    = ws2811_read,
    .write   = ws2811_write,
    .release = ws2811_release
};

/*描述杂项设备的结构体*/
struct miscdevice ws2811_misc_dev={
.minor = MISC_DYNAMIC_MINOR, //分配次设备号 ，主设备号为10       
.name  ="ws2811_dev",        //设备节点的名称查看/dev
.fops  =&misc_fops
};

/*************platform_driver内容处理************/
int ws2811_probe(struct platform_device*pdev)
{
    int misc_reg;
    int ret;
/*获取设备节点*/
    test_device_node=of_find_node_by_path("/ws2811_led");//获取节点的信息，/test取绝对路径
        if(test_device_node==NULL)
        {
            printk("test_device_node error!\n");
            return -1;
        }
    printk("test_device_node name is %s",test_device_node->name);
/*获取gpio编号并配置方向与属性*/
    ws2811_gpio =of_get_named_gpio(test_device_node,"ws2811-gpio",0);//获取GPIO编号，会将设备树中类似的属性信息转换对应的GPIO编号
    if(ws2811_gpio <0)
    {
        printk("of_get_named_gpio error!\n");
        return -1;
    }
    printk("ws2811_gpio is:%d\n",ws2811_gpio);

    ret=gpio_request(ws2811_gpio,"ws2811"); //申请一个gpio管脚
    if(ret <0)
    {
        printk("gpio_request error!\n");
        return -1;
    }
   
    gpio_direction_output(ws2811_gpio,1);//设置方向为输出

/*注册杂项设备驱动*/
    misc_reg = misc_register(&ws2811_misc_dev);//注册杂项设备，自动生成设备节点
    if(misc_reg<0)
    {
        printk("misc register is error\n");
        return -1;
    }
    printk("misc register is ok\n");

    RGB_Solid_on(1);
    printk("RGB_Solid_on is ok\n");
    return 0;
}

/*退出后注销杂项设备*/
int ws2811_remove(struct platform_device*pdev)
{
    misc_deregister(&ws2811_misc_dev);
    printk("misc exit in remove!\n");
    printk("beep_remove byb!\n");
    RGB_Solid_on(3);
    return 0;
}
/*匹配设备树*/
const struct platform_device_id beep_idtable={  //优先级更高的命名
    .name="ws2811_led"
};

/*.driver中的变量，匹配设备树的节点compatible属性*/
const struct of_device_id of_match_table_test[]={  
    {.compatible="ws2811_led"},
    {}
};

/*平台总线模型初始化*/
struct platform_driver ws2811_driver={   //通过.name名字与device匹配成功后会执行prode函数
    .probe =ws2811_probe,
    .remove=ws2811_remove,
    .driver={                           //该结构体的变量是能够通过自身的变量获取到设备树文件的信息
        .owner = THIS_MODULE,
        .name= "ws2811_led",
        .of_match_table=of_match_table_test
    },
    .id_table = &beep_idtable           //该变量比.driver中的.name优先级要高
};


/*功能实现*/
static int ws2811_driver_init(void)
{      
    int plat_reg=0;
    plat_reg =platform_driver_register(&ws2811_driver);
    if(plat_reg<0){
        printk("platform_driver_register error!\n");
    }
    printk("platform_driver_register ok!\n");

    return 0;
    //映射地址在init函数内这样加载上驱动就映射完成了
    // vir_gpio5_dr=ioermap();
}

static void ws2811_driver_exit(void)//退出函数没有返回值
{
    platform_driver_unregister(&ws2811_driver);
    gpio_free(ws2811_gpio);
    printk("byb byb !");  
}     

module_init(ws2811_driver_init);
module_exit(ws2811_driver_exit); 
MODULE_LICENSE("GPL");


